﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using SpaceGame.Util;
using Boundary=SpaceGame.Util.Boundary;

namespace SpaceGame.Gameplay.AI.Pathfinding
{
    public class NavigationMesh 
    {
        /// <summary>
        /// Keep references to nodes.
        /// </summary>
        private readonly List<MeshNode> nodeSet;

        public ReadOnlyCollection<MeshNode> Meshes
        {
            get { return nodeSet.AsReadOnly(); }
        }

        public NavigationMesh()
        {
            nodeSet = new List<MeshNode>();
        }

        /// <summary>
        /// Determine in which mesh node the corresponding point is located.
        /// </summary>
        /// <param name="point"></param>
        /// <returns></returns>
        public MeshNode FindEnclosingMeshNode(Vector2 point)
        {
            foreach(MeshNode node in nodeSet)
            {
                if(node.Polygon.ContainsPoint(point))
                {
                    return node;
                }
            }
            
            throw new ArgumentException("point must be inside a polygon mesh.");
        }


        #region Add Nodes
        /// <summary>
        /// Adds a new MeshNode instance to the Graph
        /// </summary>
        /// <param name="node">The MeshNode instance to add.</param>
        public void AddNode(MeshNode node)
        {
            // adds a node to the graph
            nodeSet.Add(node);
        }

        public void AddNodes(params MeshNode[] nodes)
        {
            // adds a node to the graph
            foreach (MeshNode node in nodes)
                nodeSet.Add(node);
        }

        /// <summary>
        /// Adds a new value to the graph.
        /// </summary>
        /// <param name="value">The value to add to the graph</param>
        public void AddNode(Util.Polygon value)
        {
            nodeSet.Add(new MeshNode(value));
        }
        #endregion

        #region Add connections
        public void AddConnection(MeshNode from, MeshNode to, Boundary boundary)
        {
            from.Neighbors.Add(to);
            to.Neighbors.Add(from);
            from.Boundaries.Add(boundary);
            to.Boundaries.Add(boundary);
        }

        /// <summary>
        /// Adds an undirected edge from one GraphNode to another with an associated cost. 
        /// </summary>
        /// <param name="from">One of the MeshNodes that is joined by the edge.</param>
        /// <param name="to">One of the MeshNodes that is joined by the edge.</param>
        public void AddConnection(MeshNode from, MeshNode to)
        {
            Boundary boundary;
            if (from.Polygon.FindBoundary(to.Polygon, out boundary))
            {
                AddConnection(from, to, boundary);
            }
            else
            {
                Debug.Assert(false, "polygon needs to have common boundary.");
            }
            
        }
        #endregion
    }


   

    

    public class MeshNode 
    {
        private List<int> costs;
        private List<Boundary> boundaries;
        private List<MeshNode> neighbors;
        
        public MeshNode(Util.Polygon value)
        {
            Polygon = value;
        }

        public Polygon Polygon
        {
            get; private set;
        }
        
        
        public List<MeshNode> Neighbors
        {
            get
            {
                if (neighbors == null)
                    neighbors = new List<MeshNode>();

                return neighbors;
            }

        }

        public List<Boundary> Boundaries
        {
            get
            {
                if (boundaries == null)
                    boundaries = new List<Boundary>();

                return boundaries;
            }
        }

        public List<int> Costs
        {
            get
            {
                if (costs == null)
                    costs = new List<int>();

                return costs;
            }
        }
    }


}
